/* Représente un arbre DTD en mémoire.
 */


#include "DtdTree.h"


DtdTree::DtdTree()
{
	/* Bloc vide */
}

DtdTree::~DtdTree()
{
	std::map<std::string , DtdDeclaration*>::iterator it;
	for ( it = elements.begin() ; it != elements.end(); it++ )
	{
		delete it->second;
	}
}

/* Ajoute une déclaration à la liste des éléments.
 * L'élément ajouté ne doit pas déjà être présent dans l'arbre.
 * Renvoie true si l'opération c'est bien passé, 'false' sinon
 * (car un élément de même nom est déjà dans l'arbre).
 */
bool DtdTree::addDeclaration(DtdDeclaration *p_dec)
{
	if (elements.find(p_dec->name) == elements.end())
	{
		elements[p_dec->name] = p_dec;

		return true;
	}

	return false;
}

/* Structure les élément en un arbre. Si tout se passe bien, true est renvoyé, sinon, false est renvoyé */
bool DtdTree::buildTreeStructure()
{
	std::map<std::string , DtdDeclaration*>::iterator it, itf;

	for (it = elements.begin(); it != elements.end(); it++)
	{
		enum TravDir travDir;
		DtdNode *n = (DtdNode*)it->second->tree;
		travDir = DOWN;
		while (n != NULL)
		{
			if (n->getNodeType() == NODE_ELEMENT)
			{
				DtdElement *e = (DtdElement*)n;
				itf = elements.find(e->name);
				if (itf == elements.end())
				{
					return false;
				}
				e->declaration = itf->second;
				n = n->parent;
				travDir = UP;
			}
			else
			{
				DtdComposite* cp = (DtdComposite*)n;
				if (travDir == DOWN)
				{
					cp->setItToBeg();
					n = cp->getItNext();
					travDir = DOWN;
				}
				else
				{
					if (cp->itContinue())
					{
						n = cp->getItNext();
						travDir = DOWN;
					}
					else
					{
						n = n->parent;
						travDir = UP;
					}
				}
			}
		} /* FIN While */
	} /* Fin FOR */

	return true;
}

/* Renvoie le fichier DTD sous la forme d'une chaine de caractères bien
 * structurée.
 */
std::string DtdTree::getDtdAsString() const
{
	std::string s;

	std::map<std::string, DtdDeclaration*>::const_iterator cit;

	for (cit = elements.begin(); cit != elements.end(); cit++)
	{

		DtdDeclaration *dec = cit->second;
		addElement(dec, s);
		s += "\n";
		dec->setAttrItToBeg();
		while (dec->attrItContinue())
		{
			addAttribut(dec->name, dec->getAttrItNext(), s);
			s += "\n";
		}
	}

	return s;
}

/* Obtenir l'expression régulière associé à une déclaration
 * Renvoie true si p_elmName correspond à un élément connue, 'false' sinon.
 */
bool DtdTree::getDtdRegex(const std::string &p_elmName, std::string &s) const
{
	std::map<std::string, DtdDeclaration*>::const_iterator cit;

	cit = elements.find(p_elmName);
	if (cit == elements.end())
	{
		return false;
	}

	addElement(cit->second, s, true);

	return true;
}

/* Obtenir la liste des nom d'attribut sous la forme d'un tableau.
 * Renvoie true si p_elmName correspond à un élément connue, 'false' sinon.
 */
bool DtdTree::getAttrSet(const std::string &p_elmName, std::set<std::string> &attrSet) const
{
	std::map<std::string, DtdDeclaration*>::const_iterator cit;

	cit = elements.find(p_elmName);
	if (cit == elements.end())
	{
		return false;
	}

	DtdDeclaration *dec = cit->second;
	dec->setAttrItToBeg();
	while (dec->attrItContinue())
	{
		attrSet.insert(dec->getAttrItNext());
	}

	return true;
}


/* Ajoute à la chaine de caractère une ligne contenant la chaine
 * de caractère associée à un élément :
 * <!ELEMENT ... ... >
 */
 void DtdTree::addElement(const DtdDeclaration *p_dec, std::string& s, bool p_regex) const
 {
 	/* En tête */
 	if (!p_regex)
 	{
 		s += "<!ELEMENT " + p_dec->name + " ";
 	}

 	enum ElementType type = p_dec->elementType;
 	if (type == ELM_EMPTY)
 	{
 		if (!p_regex) s += "EMPTY";
 	}
 	else if (type == ELM_ANY)
 	{
 		if (!p_regex) s += "ANY";
 	}
 	else if (type == ELM_PCDATA)
 	{
 		DtdComposite *t = (DtdComposite*)p_dec->tree;
 		if (t != NULL)
 		{ /* Cas mixed */
 			t->setItToBeg();
 			if (!p_regex)
 			{
 				s += "(#PCDATA";
 			}
 			else
 			{
 				s += "((\"TEXTE\";)";
 			}
 			DtdNode *n;
 			while (t->itContinue())
 			{
 				n = t->getItNext();
 				DtdElement *e = (DtdElement*)n;
 				if (p_regex)
 				{
 					s += "|(" + e->name + ";)";
 				}
 				else
 				{
 					s += "|" + e->name;
 				}
  			}
  			s += ")*";
 		}
 		else
 		{ /* Cas non mixed */
 			if (!p_regex)
 			{
 				s += "(#PCDATA)";
 			}
 			else
 			{
 				s += "\"TEXTE\";";
 			}
 		}
 	}
 	else
 	{
 		DtdNode *n = (DtdNode*)p_dec->tree;
 		/* On va au fond à droite de l'arbe */
 		while (n->getNodeType() == NODE_COMPOSITE)
 		{
 			DtdComposite *cp = (DtdComposite*)n;
 			cp->setItToBeg();
 			n = cp->getItNext();
 			s += "(";
 		}
 		/* On remonte en générant la chaine de caractères */
 		enum TravDir travDir;
 		while (n != NULL)
 		{
 			if (n->getNodeType() == NODE_ELEMENT)
 			{
 				DtdElement *e = (DtdElement*)n;
 				if (p_regex)
 				{
 					s += "(" + e->name + ";)";
 				}
 				else
 				{
 					s += e->name;
 				}
 				addCardChar(e->card, s);

 				n = n->parent;
 				travDir = UP;
 			}
 			else if (n->getNodeType() == NODE_COMPOSITE)
 			{
 				DtdComposite *cp = (DtdComposite*)n;
				if (travDir == DOWN)
				{
					cp->setItToBeg();
					s += "(";
					n = cp->getItNext();
					travDir = DOWN;
				}
				else
				{
	 				if (cp->itContinue())
	 				{
		 				enum CompositeType ct = cp->compositeType;
		 				if (ct == SEQ)
		 				{
		 					if (!p_regex) s += ",";
		 				}
		 				else
		 				{
		 					s += "|";
		 				}
		 				n = cp->getItNext();
		 				travDir = DOWN;
	 				}
	 				else
	 				{
						s += ")";
		 				addCardChar(cp->card, s);

		 				n = n->parent;
		 				travDir = UP;
	 				}
				}
  			} /* FIN CAS COMPOSITE */
 		} /* FIN WHILE */
 	}
 	if (!p_regex) s += " >";
 }

 /* Renvoie le caractère associée à une cardinalité */
void DtdTree::addCardChar(enum NodeCard c, std::string &s) const
{
	if (c == ONE_PLUS)
	{
		s += "+";
	}
	else if (c == ZERO_PLUS)
	{
		s += "*";
	}
	else if (c == ZERO_ONE)
	{
		s+= "?";
	}
}

/* Ajoute la déclaration d'un attribut à une chaine de caractère :
 * <!ATTRIBUT element-name attribute-name CDATA #IMPLIED >
 */
void DtdTree::addAttribut(const std::string &p_elemName, const std::string &p_attrName,
		 	std::string &s) const
{
	s += "<!ATTLIST " + p_elemName + " " + p_attrName + " CDATA #IMPLIED >"; 
}
